package com.zt.bus.line.service;

import com.zt.bus.assistant.service.HolidayService;
import com.zt.bus.constant.BusConstant;
import com.zt.bus.line.inter.LinePlanDiscountMapper;
import com.zt.bus.line.inter.LinePlanMapper;
import com.zt.bus.line.inter.LinePlanRefundMapper;
import com.zt.bus.line.inter.LinePlanScheduleMapper;
import com.zt.bus.line.model.LinePlan;
import com.zt.bus.line.model.LinePlanDiscount;
import com.zt.bus.line.model.LinePlanRefund;
import com.zt.bus.line.model.LinePlanSchedule;
import com.zt.bus.order.model.OrderDetailPassenger;
import com.zt.bus.order.service.OrderBackService;
import com.zt.bus.order.service.OrderService;
import com.zt.servlet.ReqBean;
import com.zt.system.model.BusinessException;
import com.zt.system.model.SqlBuilder;
import com.zt.system.service.BaseService;
import com.zt.util.DateUtil;
import com.zt.util.StringUtil;

import net.sf.json.JSONArray;
import net.sf.json.JSONObject;

import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

import java.util.*;

@Service
public class LinePlanService extends BaseService {

    @Autowired
    private LinePlanMapper linePlanMapper;

    @Autowired
    private LinePlanScheduleMapper linePlanScheduleMapper;

    @Autowired
    private LinePlanDiscountMapper linePlanDiscountMapper;

    @Autowired
    private LinePlanRefundMapper linePlanRefundMapper;

    @Autowired
    private HolidayService holidayService;

    @Autowired
    private OrderService orderService;

    @Autowired
    private OrderBackService orderBackService;

    /**
     * 查询
     * 
     * @param reqBean
     * @return
     */
    public JSONObject getTable(ReqBean reqBean) {
        SqlBuilder sqlBuilder = new SqlBuilder(
                "select l.LINE_NAME, lp.*  from DZ_LINE_PLAN lp LEFT JOIN DZ_LINE l on lp.LINE_ID=l.LINE_ID  where lp.C_PLAN_STATUS = ?",
                BusConstant.LinePlanStatus.HAS_BEEN_OPENED);
        sqlBuilder.addWhereLike2("l.LINE_NAME", reqBean.getInfo("lineName"));
        sqlBuilder.appendSql(" ORDER BY l.LINE_ID,lp.DEPART_TIME");
        return queryTable(sqlBuilder, reqBean.getPageSize(), reqBean.getPageIndex());
    }

    /**
     * 获取线路方案，没有判断状态
     * 
     * @param planId
     * @return
     */
    @Cacheable(value = BusConstant.Cache.LINE, key = "'plan_' + #planId")
    public LinePlan getById(String planId) {
        if (StringUtils.isBlank(planId)) {
            throw new BusinessException("获取主键失败");
        }
        LinePlan linePlan = linePlanMapper.selectByPrimaryKey(planId);
        return linePlan;
    }

    /**
     * 获取非删除状态的线路方案
     * 
     * @param lineId
     * @return
     */
    @Cacheable(value = BusConstant.Cache.LINE, key = "#lineId + '_plan'")
    public List<LinePlan> getByLineId(String lineId) {
        return linePlanMapper.selectByLineId(lineId);
    }

    /**
     * 添加线路方案
     * 
     * @param linePlan
     * @param departDates
     * @param linePlanDiscounts
     * @param linePlanRefund
     */
    @CacheEvict(value = BusConstant.Cache.LINE, allEntries = true)
    public void add(LinePlan linePlan, List<Date> departDates, List<LinePlanDiscount> linePlanDiscounts,
            LinePlanRefund linePlanRefund) {
        if (StringUtils.isBlank(linePlan.getLineId())) {
            throw new BusinessException("获取线路ID失败");
        }
        // 判断方案是否存在
        List<LinePlan> list = getByLineId(linePlan.getLineId());
        for (LinePlan plan : list) {
            if (plan.getDepartTime().equals(linePlan.getDepartTime())) {
                throw new BusinessException(linePlan.getDepartTime() + "发车方案已存在！");
            }
        }

        Date date = new Date();
        linePlan.setPlanId(intIncrementId("DZ_LINE_PLAN", "PLAN_ID"));
        linePlan.setMakeTime(date);
        linePlan.setModifyTime(date);
        linePlanMapper.insert(linePlan);

        // 发车日期
        addLinePlanSchedule(linePlan.getPlanId(), departDates, date);

        // 折扣方案
        if (linePlanDiscounts != null && linePlanDiscounts.size() > 0) {
            Map<String, LinePlanDiscount> map = new HashMap<>();
            for (LinePlanDiscount planDiscount : linePlanDiscounts) {
                map.put(planDiscount.getDiscountId(), planDiscount);
            }
            if (map.size() != linePlanDiscounts.size()) {
                throw new BusinessException("折扣方案选择重复！");
            }
            for (LinePlanDiscount planDiscount : linePlanDiscounts) {
                addLinePlanDiscount(linePlan.getPlanId(), planDiscount, date, linePlan.getOperator());
            }
        }
        // 退款方案
        addLinePlanRefund(linePlan.getPlanId(), linePlanRefund, date, linePlan.getOperator());
    }

    /**
     * 修改前校验方案状态
     * 
     * @param planId
     * @param status
     * @return
     */
    public JSONObject beforeUpdate(String planId, String status) {
        LinePlan plan = getById(planId);
        if (plan == null) {
            throw new BusinessException("方案不存在！");
        }
        JSONObject jsonObject = new JSONObject();
        if (plan.getPlanStatus().equals(status) || BusConstant.LinePlanStatus.OUTAGE.equals(plan.getPlanStatus())) {
            // 方案状态没变，或者是由停运转待开或已开时
            jsonObject.put("validate", "1");
        } else if (BusConstant.LinePlanStatus.HAS_BEEN_OPENED.equals(plan.getPlanStatus())
                && BusConstant.LinePlanStatus.TO_BE_OPEN.equals(status)) {
            throw new BusinessException("已开线路不能转待开！");
        } else if (BusConstant.LinePlanStatus.TO_BE_OPEN.equals(plan.getPlanStatus())
                && BusConstant.LinePlanStatus.HAS_BEEN_OPENED.equals(status)) {
            // 待开转已开
            List<OrderDetailPassenger> list = orderService.getNotDepartOrders(planId);
            if (list.size() > 0) {
                jsonObject.put("validate", "0");
                jsonObject.put("validateMsg", "该方案已招募到的" + list.size() + "位乘客将转为正式订单，确认要转为已开吗？");
                return jsonObject;
            }
        } else if (BusConstant.LinePlanStatus.OUTAGE.equals(status)
                && (BusConstant.LinePlanStatus.HAS_BEEN_OPENED.equals(plan.getPlanStatus()) || BusConstant.LinePlanStatus.TO_BE_OPEN
                        .equals(plan.getPlanStatus()))) {
            // 待开或已开转停运
            List<OrderDetailPassenger> list = orderService.getNotDepartOrders(planId);
            if (list.size() > 0) {
                jsonObject.put("validate", "0");
                jsonObject.put("validateMsg", "该操作将为" + list.size() + "位未乘坐乘客进行退款，确认要停运吗？");
                return jsonObject;
            }
        } else {
            throw new BusinessException("方案状态转变错误！");
        }
        jsonObject.put("validate", "1");
        return jsonObject;
    }

    /**
     * 修改方案计划
     * 
     * @param linePlan
     * @param departDates
     * @param linePlanDiscounts
     * @param linePlanRefund
     */
    @CacheEvict(value = BusConstant.Cache.LINE, allEntries = true)
    public void update(LinePlan linePlan, List<Date> departDates, List<LinePlanDiscount> linePlanDiscounts,
            LinePlanRefund linePlanRefund) {
        String planId = linePlan.getPlanId();
        if (StringUtil.isEmpty(planId)) {
            throw new BusinessException("获取主键失败");
        }
        // 判断方案是否存在
        List<LinePlan> list = getByLineId(planId);
        for (LinePlan plan : list) {
            if (plan.getDepartTime().equals(linePlan.getDepartTime()) && !plan.getPlanId().equals(linePlan.getPlanId())) {
                throw new BusinessException(linePlan.getDepartTime() + "发车方案已存在！");
            }
        }
        LinePlan tmpPlan = getById(planId);
        if (BusConstant.LinePlanStatus.OUTAGE.equals(linePlan.getPlanStatus())
                && (BusConstant.LinePlanStatus.HAS_BEEN_OPENED.equals(tmpPlan.getPlanStatus()) || BusConstant.LinePlanStatus.TO_BE_OPEN
                        .equals(tmpPlan.getPlanStatus()))) {
            // 待开或已开转停运，直接退款
            orderBackService.closePlan(planId);
        } else if (BusConstant.LinePlanStatus.TO_BE_OPEN.equals(tmpPlan.getPlanStatus())
                && BusConstant.LinePlanStatus.HAS_BEEN_OPENED.equals(linePlan.getPlanStatus())) {
            // 待开转已开，修改订单日期
            if (linePlan.getStartDate().before(new Date())) {
                throw new BusinessException("正式发车日期不能早于今天！");
            }
            orderService.planToBeOpen(planId, linePlan.getStartDate());
        }
        linePlan.setModifyTime(new Date());
        linePlanMapper.updateByPrimaryKeySelective(linePlan);

        // 发车日期
        List<LinePlanSchedule> dbScheduleList = linePlanScheduleMapper.selectByPlanIdAndDepartDateBetween(planId,
                new Date(), getEndDayOfMonthAfter(6));
        for (LinePlanSchedule schedule : dbScheduleList) {
          schedule.setStatus(BusConstant.Status.DISABLE);
          schedule.setModifyTime(linePlan.getModifyTime());
          schedule.setOperator(linePlan.getOperator());
            linePlanScheduleMapper.updateByPrimaryKey(schedule);
        }
        // 插入需要添加的，修改无效的
        addLinePlanSchedule(planId, departDates, linePlan.getModifyTime());

        // 折扣方案
        Map<String, LinePlanDiscount> map = new HashMap<>();
        List<LinePlanDiscount> dbList = getLinePlanDiscounts(planId);
        for (LinePlanDiscount planDiscount : dbList) {
            map.put(planDiscount.getDiscountId(), planDiscount);
        }

        if (linePlanDiscounts != null && linePlanDiscounts.size() > 0) {
            Map<String, LinePlanDiscount> tmpMap = new HashMap<>();
            for (LinePlanDiscount planDiscount : linePlanDiscounts) {
                tmpMap.put(planDiscount.getDiscountId(), planDiscount);
            }
            if (tmpMap.size() != linePlanDiscounts.size()) {
                throw new BusinessException("折扣方案选择重复！");
            }
            for (LinePlanDiscount planDiscount : linePlanDiscounts) {
                LinePlanDiscount planDiscountDb = map.get(planDiscount.getDiscountId());
                if (planDiscountDb != null) {// 存在相同的折扣方案，修改，只是修改生效时间
                    if (planDiscountDb.getExpirationDate().getTime() != planDiscount.getExpirationDate().getTime()) {
                        planDiscountDb.setExpirationDate(planDiscount.getExpirationDate());
                        planDiscountDb.setModifyTime(linePlan.getModifyTime());
                        planDiscountDb.setOperator(linePlan.getOperator());
                        linePlanDiscountMapper.updateByPrimaryKey(planDiscountDb);
                    }
                    map.remove(planDiscountDb.getDiscountId());
                } else {// 新增
                    addLinePlanDiscount(linePlan.getPlanId(), planDiscount, linePlan.getModifyTime(),
                            linePlan.getOperator());
                }
            }
        }
        // 原来存在的，现在不存在了，修改状态为失效
        for (LinePlanDiscount planDiscountDb : map.values()) {
            planDiscountDb.setStatus(BusConstant.LinePlanDiscountStatus.DISABLE);
            planDiscountDb.setModifyTime(linePlan.getModifyTime());
            planDiscountDb.setOperator(linePlan.getOperator());
            linePlanDiscountMapper.updateByPrimaryKey(planDiscountDb);
        }

        // 退款方案
        addLinePlanRefund(linePlan.getPlanId(), linePlanRefund, linePlan.getModifyTime(), linePlan.getOperator());
    }

    /**
     * 保存方案发车日期
     * 
     * @param planId
     * @param departDates
     * @param currentDate
     */
    private void addLinePlanSchedule(String planId, List<Date> departDates, Date currentDate) {
        for (Date departDate : departDates) {
            LinePlanSchedule schedule = new LinePlanSchedule();
            schedule.setId(stringId());
            schedule.setPlanId(planId);
            schedule.setDepartDate(departDate);
            schedule.setStatus(BusConstant.Status.ENABLE);
            schedule.setMakeTime(currentDate);
            schedule.setModifyTime(currentDate);
            linePlanScheduleMapper.insert(schedule);
        }
    }

    /**
     * 新增折扣方案
     * 
     * @param planId
     * @param planDiscount
     * @param date
     * @param operator
     */
    private void addLinePlanDiscount(String planId, LinePlanDiscount planDiscount, Date date, String operator) {
        planDiscount.setId(intIncrementId("DZ_LINE_PLAN_DISCOUNT", "ID"));
        planDiscount.setPlanId(planId);
        planDiscount.setStatus(BusConstant.LinePlanDiscountStatus.ENABLE);
        planDiscount.setMakeTime(date);
        planDiscount.setModifyTime(date);
        planDiscount.setOperator(operator);
        linePlanDiscountMapper.insert(planDiscount);
    }

    /**
     * 添加退款方案
     * 
     * @param planId
     * @param planRefund
     * @param date
     * @param operator
     */
    private void addLinePlanRefund(String planId, LinePlanRefund planRefund, Date date, String operator) {
        if (planRefund == null) {
            throw new BusinessException("请设置退款方案");
        }
        LinePlanRefund planRefundDb = getLinePlanRefund(planId);
        if (planRefundDb != null) {// 存在历史
            if (planRefund.getRefundId().equals(planRefundDb.getRefundId())) {// 同一个方案，修改
                if (!planRefundDb.getExpirationDate().equals(planRefund.getExpirationDate())) {
                    planRefundDb.setExpirationDate(planRefund.getExpirationDate());
                    planRefundDb.setModifyTime(date);
                    planRefundDb.setOperator(operator);
                    linePlanRefundMapper.updateByPrimaryKey(planRefundDb);
                }
                return;
            } else {// 不同的方案，删除原方案
                planRefundDb.setStatus(BusConstant.LinePlanDiscountStatus.DISABLE);
                planRefundDb.setModifyTime(date);
                planRefundDb.setOperator(operator);
                linePlanRefundMapper.updateByPrimaryKey(planRefundDb);
            }
        }
        planRefund.setId(intIncrementId("DZ_LINE_PLAN_REFUND", "ID"));
        planRefund.setPlanId(planId);
        planRefund.setStatus(BusConstant.LinePlanDiscountStatus.ENABLE);
        planRefund.setMakeTime(date);
        planRefund.setModifyTime(date);
        planRefund.setOperator(operator);
        linePlanRefundMapper.insert(planRefund);
    }

    /**
     * 删除方案计划（逻辑删除）
     * 
     * @param planId
     */
    @CacheEvict(value = BusConstant.Cache.LINE, allEntries = true)
    public void delete(String planId) {
        if (StringUtil.isEmpty(planId)) {
            throw new BusinessException("获取主键失败！");
        }
        LinePlan linePlan = linePlanMapper.selectByPrimaryKey(planId);
        if (linePlan == null) {
            throw new BusinessException("方案不存在！");
        }
        // 判断是否有预订
        if (orderService.isHasEnableOrder(planId)) {
            throw new BusinessException("该方案已存在订单！");
        }

        linePlan.setPlanStatus(BusConstant.LinePlanStatus.DELETE); // 设置为删除状态
        linePlanMapper.updateByPrimaryKeySelective(linePlan);
    }

    /**
     * 获取方案折扣
     * 
     * @param planId
     * @return
     */
    @Cacheable(value = BusConstant.Cache.LINE, key = "#planId + '_plan_discounts'")
    public List<LinePlanDiscount> getLinePlanDiscounts(String planId) {
        if (StringUtil.isEmpty(planId)) {
            throw new BusinessException("获取主键失败");
        }

        return linePlanDiscountMapper.selectEnableByPlanId(planId);
    }

    /**
     * 获取方案扣款方案
     * 
     * @param planId
     * @return
     */
    @Cacheable(value = BusConstant.Cache.LINE, key = "#planId + '_plan_Refund'")
    public LinePlanRefund getLinePlanRefund(String planId) {
        if (StringUtil.isEmpty(planId)) {
            throw new BusinessException("获取主键失败");
        }

        return linePlanRefundMapper.selectEnableByPlanId(planId);
    }

    /**
     * 获取方案半年的发车日期
     * 
     * @param planId
     * @return
     */
    public List<LinePlanSchedule> getLinePlanSchedule(String planId) {
        return linePlanScheduleMapper.selectByPlanIdAndDepartDateBetween(planId, new Date(), getEndDayOfMonthAfter(6));
    }

    /**
     * 获取方案可预订日期
     * 
     * @param planId
     * @param startDate
     * @return
     */
    public List<Date> getCanBookDays(String planId, Date startDate) {
        LinePlan linePlan = getById(planId);
        if (linePlan == null) {
            throw new BusinessException("方案不存在！");
        }
        List<Date> list = new ArrayList<>();
        // 判断当天还能不能定
        String dateStr = DateUtil.formatDateTime(DateUtil.addMinute(startDate, linePlan.getBeforeMinute()));
        String startStr = dateStr.substring(0, 11) + linePlan.getDepartTime() + dateStr.substring(16);
        startDate = DateUtil.parseDateTime(startStr);
        if (DateUtil.compare(dateStr, startStr, DateUtil.FORMAT_DATE_TIME) > 0) {
            startDate = DateUtil.addDay(startDate, 1);
        }

        if (BusConstant.LinePlanStatus.TO_BE_OPEN.equals(linePlan.getPlanStatus())) {// 新线招募
            List<LinePlanSchedule> schedules = linePlanScheduleMapper.selectByPlanIdAndDepartDateBetween(
                    linePlan.getPlanId(), startDate, getEndDayOfMonthAfter(3));
            if (schedules.size() < 22) {
                throw new BusinessException("方案可预订日期失败！");
            }
            for (int i = 0; i < 22; i++) {
                list.add(schedules.get(i).getDepartDate());
            }
        } else {
            if (linePlan.getStartDate() != null && startDate.before(linePlan.getStartDate())) {
                startDate = linePlan.getStartDate();
            }

            List<LinePlanSchedule> schedules = linePlanScheduleMapper.selectByPlanIdAndDepartDateBetween(
                    linePlan.getPlanId(), startDate, getEndDayOfMonthAfter(2));
            for (LinePlanSchedule schedule : schedules) {
                list.add(schedule.getDepartDate());
            }
        }
        return list;
    }
    
    /**
     * 查询线路可预订座位数
     * @param planId
     * @param rideDates
     * @return
     */
    public JSONArray getBookableSeatingNumOfPlan(String planId, List<Date> rideDates){
        LinePlan linePlan = getById(planId);
        if (linePlan == null) {
            throw new BusinessException("方案不存在！");
        }
        JSONArray result = new JSONArray();
        if(rideDates != null && rideDates.size() > 0 && linePlan.getSeating() > 0){
            StringBuffer sql = new StringBuffer("select b.PLAN_ID,date_format(a.RIDE_DATE,'%Y-%m-%d') date,COUNT(*) AS seating ");
            sql.append(" from DZ_ORDER_DETAIL a left join DZ_ORDER b on a.ORDER_ID = b.ORDER_ID ");
            sql.append(" where b.PLAN_ID = ?");
            sql.append(" and a.C_ORDER_DETAIL_STATUS not in (?,?,?)");
            sql.append(" and date_format(a.RIDE_DATE, '%Y-%m-%d') in (");
                for(int i = 0 ;i <rideDates.size(); i++){
                    String d = DateUtil.toString(rideDates.get(i));
                    sql.append(i == 0 ? "" : ",");
                    sql.append("'").append(d).append("'");
                }
            sql.append(" )");
            sql.append(" group by b.PLAN_ID,a.RIDE_DATE");
            result = this.query(sql.toString(), planId, BusConstant.OrderDetailStatus.TIMEOUT_OFF, 
                    BusConstant.OrderDetailStatus.CLOSED, 
                    BusConstant.OrderDetailStatus.REFUND_SUCCESS);
        }
        
        JSONArray l = new JSONArray();
        for(int i = 0 ;i <rideDates.size(); i++){
            String d = DateUtil.toString(rideDates.get(i));
            JSONObject jsonObject = new JSONObject();
            jsonObject.put("date", d);
            jsonObject.put("seating", linePlan.getSeating());
            for(Object r : result){
                JSONObject ro = (JSONObject)r;
                if(d.equals(ro.optString("date"))){
                    jsonObject.put("seating", linePlan.getSeating() - ro.optInt("seating"));
                    break;
                }
            }
            l.add(jsonObject);
        }
        return l;
    }

    private Date getEndDayOfMonthAfter(int m) {
        Calendar calendar = Calendar.getInstance();
        calendar.set(Calendar.DAY_OF_MONTH, 1);
        Date date = calendar.getTime();
        date = DateUtil.addMonth(date, m);
        date = DateUtil.addDay(date, -1);
        return date;
    }
    
    /**
     * 查询全部的方案
     * 用于订单查询页面的查询条件
     * @return
     */
	public JSONArray getAllLinePlan() {
		JSONArray resultArray = new JSONArray();
		SqlBuilder sql = new SqlBuilder(
				"select l.LINE_NAME ,lp.PLAN_ID,lp.DEPART_TIME from DZ_LINE_PLAN lp LEFT JOIN DZ_LINE l on l.LINE_ID=lp.LINE_ID ORDER BY l.LINE_NAME,lp.DEPART_TIME");
		JSONArray array = this.query(sql);
		for (int i = 0; i < array.size(); i++) {
			JSONObject json = new JSONObject();
			json.put("planId", array.getJSONObject(i).getString("planId"));
			json.put("planName", array.getJSONObject(i).getString("lineName") + "("
					+ array.getJSONObject(i).getString("departTime") + ")");
			resultArray.add(json);
		}
		return resultArray;
	}

}
